package org.idea.qiyu.framework.shutdown.listener;

import org.apache.catalina.connector.Connector;
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
import org.idea.qiyu.framework.shutdown.config.IGracefulShutdown;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;

import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Author linhao
 * @Date created in 10:29 下午 2021/7/15
 */
public class GracefulShutdownListener implements ApplicationListener, TomcatConnectorCustomizer, ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(GracefulShutdownListener.class);

    private static final AtomicBoolean destroyed = new AtomicBoolean(false);

    private ApplicationContext applicationContext;

    private volatile Connector connector;

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        if (applicationEvent instanceof ContextRefreshedEvent) {
            logger.info("容器的启动事件");
        } else if (applicationEvent instanceof ContextClosedEvent) {
            logger.info("容器的关闭事件");
            //防止重复执行
            if (!destroyed.compareAndSet(false, true)) {
                return;
            }
            this.shutdownTomcat();
            this.shutdownDubbo();
            this.shutdownOthers();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void customize(Connector connector) {
        this.connector = connector;
    }

    /**
     * 关闭一些其他的自定义应用 例如mq，线程池任务，事件处理，锁释放等等
     */
    private void shutdownOthers() {
        Collection<IGracefulShutdown> iGracefulShutdowns = applicationContext.getBeansOfType(IGracefulShutdown.class).values();
        for (IGracefulShutdown iGracefulShutdown : iGracefulShutdowns) {
            long begin = System.currentTimeMillis();
            logger.info(" ============= " + iGracefulShutdown.getName() + " 服务关闭中 ============= ");
            iGracefulShutdown.shutdown();
            long end = System.currentTimeMillis();
            logger.info(" ============= " + iGracefulShutdown.getName() + " 服务已关闭,耗时:" + (end - begin) + "ms ============= ");
        }
    }


    /**
     * 关闭Dubbo服务，将provider从注册中心上下线
     */
    private void shutdownDubbo() {
        long begin = System.currentTimeMillis();
        logger.info(" ============= Dubbo 服务关闭中 ============= ");
        //将dubbo服务从注册中心上移除
        AbstractRegistryFactory.destroyAll();
        long end = System.currentTimeMillis();
        logger.info(" ============= Dubbo 服务已关闭,耗时:" + (end - begin) + "ms ============= ");
    }

    /**
     * 关闭tomcat服务，停止创建连接
     */
    private void shutdownTomcat() {
        if (this.connector == null) {
            return;
        }
        logger.info(" ============= Tomcat 服务关闭中 ============= ");
        long begin = System.currentTimeMillis();
        this.connector.pause();
        Executor executor = this.connector.getProtocolHandler().getExecutor();
        if (executor instanceof ThreadPoolExecutor) {
            ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
            threadPoolExecutor.shutdown();
            try {
                threadPoolExecutor.awaitTermination(10, TimeUnit.SECONDS);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        logger.info(" ============= Tomcat 服务已关闭，耗时:" + (end - begin) + "ms ============= ");
    }

}
